using System;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
using System.Diagnostics;
using System.Collections.Generic;
using System.Collections.Specialized;

namespace My.IdlConverter.Core.Types
{
    internal sealed class TypeTranslator
    {
        log4net.ILog log = log4net.LogManager.GetLogger(typeof(TypeTranslator));

        private string _cppTypeString;
        private static XmlDocument m_xmlDoc = new XmlDocument();

        static TypeTranslator()
        {
            m_xmlDoc.Load(Cfg.Configuration.TypeMappingFile);
        }

        public TypeTranslator()
            :this("")
        {
        }

        public TypeTranslator(string cppTypeString)
        {
            _cppTypeString = cppTypeString;
        }

        public string Consumer
        {
            get
            {
                return _cppTypeString;
            }
            set
            {
                _cppTypeString = value;
            }
        }

        public string Origin
        {
            get
            {
                string orgginType = Consumer;
                orgginType = orgginType.Replace("&", "");
                orgginType = orgginType.Replace("*", "");
                orgginType = orgginType.Replace("const", "");

                return orgginType.Trim();
            }
        }

        public IType Translate()
        {
            IType type = new IdlConverterType(Consumer);
            if (IsStlType())
            {
                TranslateStl(type);
            }
            type.Substitution = GetSubstitutionTypes();

            return type;
        }

        private void TranslateStl(IType type)
        {
            string[] simpleTypes = Util.StringUtil.SplitStl(Consumer);
            TypeTranslator typeTranslator = new TypeTranslator();

            foreach (string nextSimpleType in simpleTypes)
            {
                typeTranslator.Consumer = nextSimpleType;
                IType simpeType = typeTranslator.Translate();
                type.SubTypes.Add(simpeType);
            }

            _cppTypeString = Util.StringUtil.RemoveRange(_cppTypeString, '<', '>');
        }

        private bool IsStlType()
        {
            return TypeRecognizer.ExistStlTypes(Consumer);
        }

        private TypeMap GetSubstitutionTypes()
        {
            bool handled = false;

            string originType = Origin;
            TypeMap typeMap = new TypeMap(originType);

            XmlNodeList childNodes = m_xmlDoc.SelectNodes("//TypeMaps/TypeMap");
            foreach (XmlNode nextNode in childNodes)
            {
                XmlNode orginNode = nextNode.SelectSingleNode(".//Origin");
                XmlNodeList substitutionsNodes = nextNode.SelectNodes(".//Substitutions/Substitution");

                string[] originTypes = orginNode.InnerText.Trim().Split(';');
                if (ContainSomeType(originTypes, originType, Consumer))
                {
                    foreach (XmlNode nextSubstitutiopnNode in substitutionsNodes)
                    {
                        XmlAttribute typeAttr = nextSubstitutiopnNode.Attributes["Type"];
                        Debug.Assert(typeAttr != null);

                        Type type = Util.ReflectHelper.TypeFromAssembly(Util.TypeNameParser.Parse(typeAttr.Value));
                        IType typeObj = Util.ReflectHelper.ConstructTypeObj(type);
                        typeObj.TypeString = Consumer;
                        //temp here
                        string subTypeString = nextSubstitutiopnNode.Attributes["Value"].InnerText;
                        if (subTypeString.Equals("undefined"))
                        {
                            subTypeString = originType;
                        }
                        XmlNode convertorInNode = nextSubstitutiopnNode.SelectSingleNode(".//ConvertorIn");
                        XmlNode convertorOutNode = nextSubstitutiopnNode.SelectSingleNode(".//ConvertorOut");
                        string convertorInMethod = "";
                        string convertorOutMethod = "";
                        if (convertorInNode != null)
                        {
                            convertorInMethod = convertorInNode.InnerText.Trim();
                        }
                        if (convertorOutNode != null)
                        {
                            convertorOutMethod = convertorOutNode.InnerText.Trim();
                        }
                        typeMap.SubstitutionTypes.Add(new TypeMap.TypeSubstitution(subTypeString, typeObj, convertorInMethod, convertorOutMethod));

                        handled = true;
                        break;
                    }
                }
            }
            if (!handled)
            {
                IType typeObj = new UnknownType();
                typeObj.TypeString = Consumer;
                //temp here
                typeMap.SubstitutionTypes.Add(new TypeMap.TypeSubstitution(Origin, typeObj, "", ""));
            }

            return typeMap;
        }

        private bool ContainSomeType(string[] originTypes,string originType,string typeString)
        {
            if ( Util.ArrayHelper.FindString(originTypes, originType)
                || Util.ArrayHelper.FindString(originTypes, Consumer) )
            {
                return true;
            }
            else
            {
                foreach (string nextOriginType in originTypes)
                {
                    if (IsRegex(nextOriginType))
                    {
                        if (Regex.IsMatch(typeString, nextOriginType.Trim()))
                        {
                            return true;
                        }
                    }
                }
            }

            return false;
        }

        private bool IsRegex(string regex)
        {
            return regex.IndexOf("*")!=-1;
        }
    }
}
